 /*
	       File: frmConfigurationEditor.cs
	Create Date: 2007-08-16
	    Version:

	Description:


	 Change log:


*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;

using GuerillaProgrammer.FileProcessor.Settings;

namespace GuerillaProgrammer.FileProcessor
{
    public partial class frmConfigurationEditor : Form
    {
        #region Private member fields
        private Configuration _Configuration;
        private string _Filename;
        private bool _Modified;
        private string _ParentTreeNodeID;
        #endregion

        #region Constructors and cleanup methods
        public frmConfigurationEditor()
        {
            InitializeComponent();
            _Configuration = null;
            _Filename = string.Empty;
            _Modified = false;
        }
        #endregion

        #region Private implementation methods
        #region Configuration managment methods
        private void NewConfiguration()
        {
            _Configuration = new Configuration();
            _Configuration.Services = new ConfigurationServices();
        }
        private void AddDirectoryToConfiguration(ConfigurationDirectory directory)
        {
            _Configuration.Directories.Add(directory);
        }
        private void AddFileTypeToConfiguration(ConfigurationFileType fileType)
        {
            _Configuration.FileTypes.Add(fileType);
        }
        private void AddWorkflowToConfiguration(ConfigurationWorkflow workFlow)
        {
            _Configuration.Workflows.Add(workFlow);
        }
        private void WriteConfigurationToFile(string fileName)
        {
            StreamWriter writer = new StreamWriter(_Filename);
            XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
            serializer.Serialize(writer, _Configuration);
            writer.Flush();
            writer.Close();
        }
        #endregion

        #region UI Methods
        private void LoadTreeControl()
        {
            ClearPanel();
            tvConfiguration.Nodes.Clear();

            if (null == _Configuration)
            {
                return;
            }

            TreeNode configNode = new TreeNode("File Processor Configuration");
            configNode.Tag = "CONFIG";
            AddParentMenuItem(configNode);
            TreeNode fileTypeNode = new TreeNode("File Types");
            fileTypeNode.Tag = "FILETYPE";
            AddParentMenuItem(fileTypeNode);
            TreeNode directoryNode = new TreeNode("Directories");
            directoryNode.Tag = "DIRECTORY";
            AddParentMenuItem(directoryNode);
            TreeNode workFlowNode = new TreeNode("Workflows");
            workFlowNode.Tag = "WORKFLOW";
            AddParentMenuItem(workFlowNode);
            TreeNode serviceNode = new TreeNode("Services");
            serviceNode.Tag = "SERVICE";

            #region Load workflows in to the tree
            if (null != _Configuration.Workflows)
            {
                foreach (ConfigurationWorkflow workFlow in _Configuration.Workflows)
                {
                    TreeNode wfNode = new TreeNode(workFlow.Name);
                    wfNode.Tag = workFlow.ID;
                    workFlowNode.Nodes.Add(wfNode);
                    AddNodeMenuItem(wfNode);
                }
            }
            #endregion

            #region Load directories in the tree
            if (null != _Configuration.Directories)
            {
                foreach (ConfigurationDirectory directory in _Configuration.Directories)
                {
                    TreeNode dirNode = new TreeNode(directory.Name);
                    dirNode.Tag = directory.ID;
                    directoryNode.Nodes.Add(dirNode);
                    AddNodeMenuItem(dirNode);
                }
            }
            #endregion

            #region Load file types in the tree
            if (null != _Configuration.FileTypes)
            {
                foreach (ConfigurationFileType fileType in _Configuration.FileTypes)
                {
                    TreeNode ftNode = new TreeNode(fileType.Name);
                    ftNode.Tag = fileType.ID;
                    fileTypeNode.Nodes.Add(ftNode);
                    AddNodeMenuItem(ftNode);
                }
            }
            #endregion

            configNode.Nodes.Add(serviceNode);
            configNode.Nodes.Add(workFlowNode);
            configNode.Nodes.Add(fileTypeNode);
            configNode.Nodes.Add(directoryNode);
            tvConfiguration.Nodes.Add(configNode);
            tvConfiguration.ExpandAll();
        }
        private void AddNodeToTree(string parent, TreeNode node)
        {
            TreeNode parentNode = GetParentNode(parent);
            parentNode.Nodes.Add(node);
            AddNodeMenuItem(node);

            tvConfiguration.SelectedNode = node;
        }
        private TreeNode GetParentNode(string parentID)
        {
            TreeNode rootNode = tvConfiguration.Nodes[0];
            foreach (TreeNode parentNode in rootNode.Nodes)
            {
                if (parentID == (string) parentNode.Tag)
                {
                    return parentNode;
                }
            }

            throw new ApplicationException("Cannot find parent node");
        }
        private TreeNode GetChildNode(TreeNode parentNode, string childID)
        {
            if(0 == parentNode.Nodes.Count)
            {
                return null;
            }
            
            foreach(TreeNode childNode in parentNode.Nodes)
            {
                if(childID == (string) childNode.Tag)
                {
                    return childNode;
                }
            }

            return null;
        }
        private void UpdateTreeViewItem(string displayName, string id)
        {
            //In order to update a node in the tree we have to remove it and reinsert it.
            int index = 0;
            TreeNode parentNode = GetParentNode(_ParentTreeNodeID);
            TreeNode childNode = GetChildNode(parentNode, id);

            TreeNode updatedNode = new TreeNode();
            updatedNode.Text = displayName;
            updatedNode.Tag = id;

            if(null != childNode)
            {
                index = childNode.Index;
                parentNode.Nodes.Remove(childNode);
            }
            
            parentNode.Nodes.Insert(index, updatedNode);
        }

        private void ClearPanel()
        {
            splitContainer1.Panel2.Controls.Clear();
        }

        void DeleteFileType_Click(object sender, EventArgs e)
        {
            TreeNode nodeToDelete = tvConfiguration.SelectedNode;
            int counter = 0;

            if (null == nodeToDelete)
            {
                return;
            }

            foreach (ConfigurationFileType fileType in _Configuration.FileTypes)
            {
                if (fileType.ID == (string)nodeToDelete.Tag)
                {
                    nodeToDelete.Remove();
                    _Configuration.FileTypes.Remove(fileType);
                    break;
                }
                counter++;
            }
        }
        void DeleteDirectory_Click(object sender, EventArgs e)
        {
            TreeNode nodeToDelete = tvConfiguration.SelectedNode;
            int counter = 0;

            if (null == nodeToDelete)
            {
                return;
            }

            foreach (ConfigurationDirectory directory in _Configuration.Directories)
            {
                if (directory.ID == (string)nodeToDelete.Tag)
                {
                    nodeToDelete.Remove();
                    _Configuration.Directories.Remove(directory);
                    break;
                }
                counter++;
            }
        }
        void DeleteWorkFlow_Click(object sender, EventArgs e)
        {
            TreeNode nodeToDelete = tvConfiguration.SelectedNode;
            int counter = 0;

            if (null == nodeToDelete)
            {
                return;
            }
            
            foreach (ConfigurationWorkflow workflow in _Configuration.Workflows)
            {
                if (workflow.ID == (string)nodeToDelete.Tag)
                {
                    nodeToDelete.Remove();
                    _Configuration.Workflows.Remove(workflow);
                    break;
                }
                counter++;
            }
        }
        private void AddNodeMenuItem(TreeNode node)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem menuItem = new MenuItem();
            
            switch ((string) node.Parent.Tag)
            {
                case "FILETYPE" :
                    menuItem.Text = "Delete FileType";
                    menuItem.Click += new EventHandler(DeleteFileType_Click);
                    break;
                case "DIRECTORY":
                    menuItem.Text = "Delete Directory";
                    menuItem.Click += new EventHandler(DeleteDirectory_Click);
                    break;
                case "WORKFLOW":
                    menuItem.Text = "Delete Workflow";
                    menuItem.Click += new EventHandler(DeleteWorkFlow_Click);
                    break;
                default:
                    return;
            }

            menu.MenuItems.Add(menuItem);
            node.ContextMenu = menu;
        }
        private void AddWorkflowMenuItem(ContextMenu menu)
        {
            MenuItem menuItem = new MenuItem();
            menuItem.Text = "Add Workflow";
            menuItem.Click += new EventHandler(addWorkflowToolStripMenuItem_Click);
            
            MenuItem menuItem1 = new MenuItem();
            menuItem1.Text = "Reload workflows";
            menuItem1.Click += new EventHandler(menuReloadWorkflows_Click);

            MenuItem menuItem2 = new MenuItem();
            menuItem2.Text = "Create new workflow";
            menuItem2.Click +=new EventHandler(menuCreateNewWorkflow_Click);

            menu.MenuItems.Add(menuItem);
            menu.MenuItems.Add(menuItem1);
            menu.MenuItems.Add(menuItem2);
        }
        private void AddParentMenuItem(TreeNode node)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem menuItem = new MenuItem();

            switch ((string)node.Tag)
            {
                case "FILETYPE":
                    menuItem.Text = "Add FileType";
                    menuItem.Click += new EventHandler(addFileTypeToolStripMenuItem_Click);
                    menu.MenuItems.Add(menuItem);
                    break;
                case "DIRECTORY":
                    menuItem.Text = "Add Directory";
                    menuItem.Click += new EventHandler(addDirectoryToolStripMenuItem_Click);
                    menu.MenuItems.Add(menuItem);
                    break;
                case "WORKFLOW":
                    AddWorkflowMenuItem(menu);
                    break;
                default:
                    return;
            }

            node.ContextMenu = menu;
        }

        private void LoadServiceControl()
        {
            ClearPanel();
            ucService serviceControl = new ucService();
            serviceControl.LoadServiceConfiguration(_Configuration.Services);
            serviceControl.UpdateServiceSettings += new ucService.UpdateServiceSettingsDelegate(this.UpdateServices);
            splitContainer1.Panel2.Controls.Add(serviceControl);
        }
        private void LoadDirectoryControl(ConfigurationDirectory configDir)
        {
            ClearPanel();
            ucDirectory directoryControl = new ucDirectory();
            directoryControl.LoadDirectory(_Configuration, configDir);
            directoryControl.FinishedUpdate += new ucDirectory.FinishedUpdateDelegate(this.UpdateTreeViewItem);
            splitContainer1.Panel2.Controls.Add(directoryControl);
        }
        private void LoadFileType(ConfigurationFileType fileType)
        {
            ClearPanel();
            ucFileType fileTypeControl = new ucFileType();
            fileTypeControl.LoadFileType(_Configuration, fileType);
            fileTypeControl.FinishedUpdate += new ucFileType.FinishedUpdateDelegate(this.UpdateTreeViewItem);
            splitContainer1.Panel2.Controls.Add(fileTypeControl);
        }
        private void LoadWorkFlow(ConfigurationWorkflow workFlow)
        {
            ClearPanel();
            ucWorkFlow workFlowControl = new ucWorkFlow();
            workFlowControl.LoadWorkflow(_Configuration, workFlow);
            workFlowControl.FinishedUpdate += new ucWorkFlow.FinishedUpdateDelegate(this.UpdateTreeViewItem);
            splitContainer1.Panel2.Controls.Add(workFlowControl);
        }
        #endregion

        #region Workflow helpers
        private TreeNode GetWorkflowNode()
        {
            TreeNode rootNode = tvConfiguration.Nodes[0];
            foreach (TreeNode node in rootNode.Nodes)
            {
                if ("WORKFLOW" == (string)node.Tag)
                {
                    return node;
                }
            }

            return null;
        }
        private void RemoveWorkflowsFromTree()
        {
            TreeNode workflowNode = GetWorkflowNode();
            if (null == workflowNode)
            {
                return;
            }
            if (null != workflowNode.Nodes)
            {
                foreach (TreeNode node in workflowNode.Nodes)
                {
                    node.Remove();
                }
            }
        }
        private void AddWorkflowToTree(ConfigurationWorkflow workflow)
        {
            TreeNode workflowNode = GetWorkflowNode();
            if (null == workflowNode)
            {
                return;
            }

            TreeNode newWorkflow = new TreeNode();
            newWorkflow.Text = workflow.Name;
            newWorkflow.Tag = workflow.ID;
            workflowNode.Nodes.Add(newWorkflow);
        }
        private void LoadWorkflows()
        {
            string[] files = Directory.GetFiles(_Configuration.Services.AssemblyDirectory, "*.dll");

            foreach (string assemblyName in files)
            {
                Assembly assemblyInstance = Assembly.LoadFile(assemblyName);
                Type[] types = assemblyInstance.GetTypes();
                foreach (Type type in types)
                {
                    if (true == type.IsSubclassOf(typeof(SequentialWorkflowActivity)))
                    {
                        //need to create the type name - 
                        string typeName = string.Format("{0},{1}",type.Name, assemblyInstance.FullName);
                        ConfigurationWorkflow workflow = new ConfigurationWorkflow();
                        workflow.WorkflowStrongName = typeName;
                        workflow.Name = type.Name;
                        workflow.ID = Guid.NewGuid().ToString();
                        _Configuration.Workflows.Add(workflow);
                        AddWorkflowToTree(workflow);
                    }
                }
            }
        }
        #endregion
        #endregion

        #region Events handlers
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null != _Configuration && true == _Modified)
            {
                //see if the user wants to save first
                if (DialogResult.Yes == MessageBox.Show("Save the configuration before exiting?", "Save?", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation))
                {
                    return;
                }
            }

            this.Close();
        }
        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            NewConfiguration();
            LoadTreeControl();
            _Modified = true;
        }
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (DialogResult.OK == ofd.ShowDialog())
            {
                _Filename = ofd.FileName;
                _Configuration = ConfigurationManager.ReadConfiguration(_Filename);
                LoadTreeControl();
            }
        }
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == _Configuration)
            {
                return;
            }

            if (string.Empty == _Filename)
            {
                SaveFileDialog sfd = new SaveFileDialog();
                if (DialogResult.OK == sfd.ShowDialog())
                {
                    _Filename = sfd.FileName;
                }
                else
                {
                    return;
                }
            }

            if (true == File.Exists(_Filename))
            {
                if(true == File.Exists(_Filename + ".backup"))
                {
                    File.Delete(_Filename + ".backup");
                }
                File.Move(_Filename, _Filename + ".backup");
            }

            WriteConfigurationToFile(_Filename);                
        }
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (null == _Configuration)
            {
                return;
            }

            SaveFileDialog sfd = new SaveFileDialog();
            if (DialogResult.OK == sfd.ShowDialog())
            {
                _Filename = sfd.FileName;
            }

            WriteConfigurationToFile(_Filename);
        }
        private void tvConfiguration_AfterSelect(object sender, TreeViewEventArgs e)
        {
            TreeNode selectedNode = e.Node;
            string nodeTag = (string)selectedNode.Tag;
            if (0 != selectedNode.Nodes.Count)
            {
                //we only care if it is a leaf node
                ClearPanel();
                return;
            }

            TreeNode parentNode = e.Node.Parent;
            string parentTag = (string)parentNode.Tag;

            _ParentTreeNodeID = parentTag;

            switch (parentTag)
            {
                case "CONFIG":
                    if ("SERVICE" == nodeTag)
                    {
                        LoadServiceControl();
                    }
                    break;
                case "DIRECTORY":
                    LoadDirectoryControl(ConfigurationManager.GetDirectoryByID(_Configuration, nodeTag));
                    break;
                case "FILETYPE":
                    LoadFileType(ConfigurationManager.GetFileTypeByID(_Configuration, nodeTag));
                    break;
                case "WORKFLOW":
                    LoadWorkFlow(ConfigurationManager.GetWorkflowByID(_Configuration, nodeTag));
                    break;
                default:
                    return;
            }
        }
        private void UpdateServices(ConfigurationServices service)
        {
            _Configuration.Services = service;
        }
        private void addWorkflowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigurationWorkflow newWorkFlow = new ConfigurationWorkflow();
            newWorkFlow.Name = "NEW";
            newWorkFlow.ID = DateTime.Now.ToString("yyyyMMddhhmmss");
            
            if (null == _Configuration.Workflows)
            {
                _Configuration.Workflows = new List<ConfigurationWorkflow>();
            }

            _Configuration.Workflows.Add(newWorkFlow);

            TreeNode workFlowNode = new TreeNode();
            workFlowNode.Text = newWorkFlow.Name;
            workFlowNode.Tag = newWorkFlow.ID;
            AddNodeToTree("WORKFLOW", workFlowNode);
        }
        private void menuReloadWorkflows_Click(object sender, EventArgs e)
        {
            //first confirm that the user wants to do this
            if (DialogResult.OK == MessageBox.Show("This will remove all of the workflows and replace them with the workflows\n in the workflows directory.", "Are you sure?", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk))
            {
                //remove all of the workflows from the configuration and tree
                _Configuration.Workflows.Clear();
                RemoveWorkflowsFromTree();
                LoadWorkflows();
            }
        }
        private void menuCreateNewWorkflow_Click(object sender, EventArgs e)
        {
            //Eventually I want this pulled inside the tool so I don't have to shell out
            string editorPath = System.Configuration.ConfigurationManager.AppSettings["WFEditor"];
            if (string.IsNullOrEmpty(editorPath))
            {
                MessageBox.Show("The path to the editor has not been configured.  please fix and try again.", "Configuration error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            System.Diagnostics.Process.Start(editorPath);

        }
        private void addFileTypeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigurationFileType newFileType = new ConfigurationFileType();
            newFileType.Name = "NEW";
            newFileType.ID = DateTime.Now.ToString("yyyyMMddhhmmss");

            if(null == _Configuration.FileTypes)
            {
                _Configuration.FileTypes = new List<ConfigurationFileType>();
            }
            _Configuration.FileTypes.Add(newFileType);
            
            TreeNode fileTypeNode = new TreeNode();
            fileTypeNode.Text = newFileType.Name;
            fileTypeNode.Tag = newFileType.ID;
            AddNodeToTree("FILETYPE", fileTypeNode);
        }
        private void addDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigurationDirectory newDirectory = new ConfigurationDirectory();
            newDirectory.Name = "NEW";
            newDirectory.ID = DateTime.Now.ToString("yyyyMMddhhmmss");

            if(null == _Configuration.Directories)
            {
                _Configuration.Directories = new List<ConfigurationDirectory>();
            }
            _Configuration.Directories.Add(newDirectory);

            TreeNode directoryNode = new TreeNode();
            directoryNode.Text = newDirectory.Name;
            directoryNode.Tag = newDirectory.ID;
            AddNodeToTree("DIRECTORY", directoryNode);
        }
        #endregion
    }
}